package com.ysh.smartplatform.service.impl;

import com.alibaba.druid.support.clickhouse.BalancedClickhouseDriver;
import com.ysh.smartplatform.constans.ResultCode;
import com.ysh.smartplatform.dto.UserDto;
import com.ysh.smartplatform.event.LoginFailEvent;
import com.ysh.smartplatform.event.LoginSuccessEvent;
import com.ysh.smartplatform.exceptions.AddDataException;
import com.ysh.smartplatform.exceptions.FindDataException;
import com.ysh.smartplatform.mapper.UserMapper;
import com.ysh.smartplatform.pojo.SysUserInfo;
import com.ysh.smartplatform.service.UserService;
import com.ysh.smartplatform.utils.MyStringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.DigestUtils;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;

/**
 * 袁胜虎 on 2022/9/5 16:21
 */
@Service
@Transactional//是声明式事务管理 编程中使用的注解
public class UserServiceImpl implements UserService {
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    public void setbCryptPasswordEncoder(BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

    private ApplicationContext context;
    @Autowired
    public void setContext(ApplicationContext context) {
        this.context = context;
    }

    private  UserMapper userMapper;

    @Autowired
    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Override
    public void addUser(UserDto userDto) {
        Assert.isTrue(!userDto.isEmpty(),()->{
            throw new AddDataException("参数为空", ResultCode.PARAM_CONTENT_EMPTY);
        });
        //需要查询一下数据是不是存在,存在需要提醒客户端,账号已经存在
        List<SysUserInfo> userInfoList = userMapper.findByUserNameOrPhoneOrEmail(userDto.getUsername(), userDto.getPhone(), userDto.getEmail());

        Assert.isTrue(userInfoList.size() == 0,()->{
            throw new AddDataException("账号已存在",ResultCode.USERNAME_ALREADY_EXIST);
        });

        //对数据进行校验,转换到SysUserInfo对象上面
        SysUserInfo sysUserInfo = new SysUserInfo();
        //拷贝数据,要求属性名一样
        // sysUserInfo.setUsername(userDto.getUsername());

        BeanUtils.copyProperties(userDto,sysUserInfo);
        //将密码转成散列值,比如MD5
        //MD5是相同的内容总是会有相同的MD5值,所以为了避免相同的密码在数据库中显示相同的内容,我们会对数据进行加盐
        //盐不能一样,因为盐如果一样和一样的密码就没有区别了,盐是我们随机生成的
        //我们随机生成一个十位的字符串

//        String salt = MyStringUtils.createRandomString(10);
//        //盐必须要保存,否则下次得到的结果不一样的话就无法校验数据了
//        sysUserInfo.setPwdSalt(salt);
//        //我们真正的密码是 salt+password+salt
//        String passwordMD5 = DigestUtils.md5DigestAsHex((salt +sysUserInfo.getPassword() + salt).getBytes(StandardCharsets.UTF_8));
        //替换密码
//        sysUserInfo.setPassword(passwordMD5);
        //自己注册的就是自己,管理员添加的就写管理员的账号
        sysUserInfo.setPassword(bCryptPasswordEncoder.encode(sysUserInfo.getPassword()));
        sysUserInfo.setCreateTime(new Date());
        sysUserInfo.setCreateBy(sysUserInfo.getUsername());
        sysUserInfo.setRemark("普通用户");

        userMapper.addUser(sysUserInfo);
    }

    @Override
    public SysUserInfo findUserByName(String userName) {
        //传递用户名
        Assert.hasText(userName,()->{
            throw new FindDataException("用户名不能为空",ResultCode.PARAM_CONTENT_EMPTY);
        });
        SysUserInfo user = userMapper.findUserByName(userName);
        return user;
    }

    @Override
    public SysUserInfo login(String userName, String password) {
        SysUserInfo sysUserInfo = findUserByName(userName);
        //将用户传递过来的密码和我们的盐一起计算出一个md5值,这个值如果和数据库查询出来的一样,则代表登录成功
        String passwordMD5 = DigestUtils.md5DigestAsHex((sysUserInfo.getPwdSalt() + password + sysUserInfo.getPwdSalt()).getBytes(StandardCharsets.UTF_8));
        Assert.isTrue(passwordMD5.equals(sysUserInfo.getPassword()),()->{
            context.publishEvent(new LoginFailEvent(null,userName));
            throw new FindDataException("用户名密码错误",ResultCode.USERNAME_PASSWORD_ERROR);
        });
        //更新日志不是当前业务的核心操作,属于辅助操作,并且成功与否不能影响前面的登录操作,所以当前操作可以放在异步中执行
        //怎么搞一个异步?
        //虽然我们的需求中并没有展示登录时间等操作,但是要记住,我们几乎所有和登录,修改等相关的操作,在表中都有对应的时间列
        //我们需要更新用户的数据,登录的时间和ip
        context.publishEvent(new LoginSuccessEvent(sysUserInfo.getuId(),sysUserInfo.getUsername()));
        return sysUserInfo;
    }

    @Override
    public void updateLoginData(Long uid, String ip) {
        userMapper.updateLoginDataByUserId(uid,ip);
    }

    @Override
    public void deleteById(String id) {
        Assert.notNull(id,()->{
            throw new AddDataException("参数为空",ResultCode.DATA_NOT_EXIST);
        });
        userMapper.deleteById(id);
    }
}
